#ifndef SERVER_H
#define SERVER_H

/** 
 *  @file server.h
 *  @brief Geolocation Project - Server Header - ENSEIRB RSR 2007, 
 *  everything is under GNU GPL v3 or later
 *  @author Jean Loup Le Roux
 *  @author http://www.nightmare-creature.org
 *
 */

#include "../protocol_visu.h"
#include "../protocol_servers_config.h"

#include <arpa/inet.h>
#include <signal.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <time.h>

/** @defgroup server Geolocation Server
*/
//@{

/// Maximum input UDP buffer size (bytes)
#define MAX_UDP_DATA 128

/// Idem Visualisation Client
#define MAX_VISU_DATA 128

/// Idem Neighbour TCP communications
#define MAX_TCP_DATA 128

/// Selects a protocol within protocol family (socket configuration)
#define SELECT_PROTOCOL 0

/// Servers config file format argument count
#define SERVERS_CFG_FORMAT_COUNT 5

/// SP format argument count 
#define CLIENT_SP_FORMAT_COUNT 6

/// Minimum port number to use
#define MINI_PORT 1025

/// Maximum number of supported servers 
#define MAX_SUPPORTED_SRV 5 

/// Maximum number of clients per server
#define MAX_CLIENTS_PER_SERVER 10

/// Timeout (seconds) for each main select() call
#define MAIN_SELECT_TIMEOUT 1 

/// Client OUT timeout (s), determines how a non updated Client should be considered OUT
#define CLIENT_OUT_TIMEOUT 30

/// Maximum hosts number in the incoming TCP connection queue
#define LISTEN_PENDING_TCP 5

/// Maximum number of neighbours per server
#define MAX_NGB 5           

/// IPv4 address buffer size (bytes)
#define IP_STR 64 

/// Banner displayed at server startup
#define SERVER_BANNER " Geolocation Server build %s %s\n Copyright (C) 2008 Jean Loup Le Roux, nightmare-creature.org\n This is free software, you are welcome to redistribute it under the terms of GNU GPL v3 or later.\n There is NO WARRANTY, to the extent permitted by law.\n"

/// Visualisation server TCP port 
#define VISU_SERVER_TCP_PORT 3002

/// Invalid Client time flag
#define INVALID_CLIENT_TIME -1

/// Invalid Client flag
#define INVALID_CLIENT -1

/// Valid Client flag
#define VALID_CLIENT 0

/// Standard number of CLI arguments + 1
#define CORRECT_ARGC 3

/** @brief Server structure (only used by Server), describes everything needed about a Geolocation Server 
 * @note 
 * Just to clear things up : latitude = y, longitude = x  
 * ... and google maps gives : (latitude, longitude).
 * Every zone is refered as (Coord a, Coord b), assuming that : 
 * a is bottom left corner and b is top right corner
 */
typedef struct _Server {

  /// Server's public IPv4 address
  char ip[IP_STR];   

  /// Server's UDP port
  short port;

  /// Server's responsability (authority) zone
  Coord a,b;

  /// TCP link to this server, if it's my neighbour
  int link;                         

  /// For network purposes, no trespassing.
  struct sockaddr_in sockaddr_link; 

  /// A server is called 'neighbour' if I share a zone with it,
  /// this is an array of neighbours ID (each cell equals to -1 if irrelevant).
  int ngb[MAX_NGB];                 

} Server;

/** @brief Client structure (only used by Server), describes everything needed about a Geolocation Client
 */
typedef struct _Client {

  /// Defines the Client's status
  clientStatus status;

  /// UNIX time of last update 
  long update_time;

  /// Client's public IPv4 address
  char ip[IP_STR];

  /// UDP return port
  unsigned int port;

  /// a-z,A-Z,0-9_ : unique Client reference
  char mac[PROTOCOL_CLIENT_MAC_SZ];

  /// Last recorded location
  Coord location;

  /// Last recorded time (s) 
  long time;

  /// Last recorded timeout (timeout(t) = time(t) + offset )
  long timeout;

} Client;

/** @brief Status Packet structure (only used by Server), this is how the Server stores the packets
 * sent by Clients using UDP unicast
 */
typedef struct _StatusPacket {

  /// Client's public IPv4 address
  char ip[IP_STR];

  /// Client's UDP return port
  unsigned int port;

  /// a-z,A-Z,0-9 : unique Client ID
  char mac[PROTOCOL_CLIENT_MAC_SZ]; 

  /// Client's location
  Coord location;

  /// Client's time
  long time;

  /// Client's timeout delay
  long timeout;

} StatusPacket;

/** @brief Displays help
*/
void Server_DisplayHelp( void );

/** @brief Displays a lot of info about a Client array
 * @param cli_array Client array
 * @param max_client_id Maximum Client ID
 */
void Server_PrintClients( Client* cli_array, int max_client_id );

/** @brief Checks if a given point is inside a server responsability zone
 * @param srv Server pointer
 * @param crd Point 
 * @return 0 (inside server zone) or -1 
 */
int Server_inZone( Server* srv, Coord* crd);

/** @brief Returns the first matching responsible server 
 * @param srv_array Array of servers
 * @param max_srv_id Maximum server ID
 * @param location My actual position
 * @return server ID or -1 if no suitable server found
 */
int Server_ResponsibleID( Server* srv_array, int max_srv_id, Coord* location );

/** @brief Establishes a link to a neighbour, with respect to the 
 * IP priority rule
 * @return 0 if successfully connected or -1 
 * @param local Local server pointer 
 * @param remote Remote server pointer
 */
int Server_NgbTcpLink( Server* local, Server* remote );

/** @brief Sets flags on a socket descriptor
 *@param sd Socket descriptor
 *@param flag Flag to set
 *@return 0 on success, or -1
 */
int Server_FlagMySocket( int sd, int flag);

/** @brief Computes neighbour servers
 * @param srv_array Servers array to compute
 * @param my_srv_id Me
 * @param max_srv_id Maximum server ID
 */
void Server_SharedZoneWeaver( Server* srv_array, int my_srv_id, int max_srv_id );

/** @brief Reads one line of the server's config file
 * @param cfgfile FILE* to read from
 * @param srv Structure to fill
 * @return -1 on error
 */
int Server_ReadConfigFile( FILE* cfgfile, Server* srv );

/** @brief Decides wether a server's responsability zone intersects with another
 * @param srv1 Server 1
 * @param srv2 Server 2
 * @return 0 if true or -1
 */
int Server_Intersect( Server* srv1, Server* srv2 );

/** @brief Displays everything about a server
 * @param srv Server to display
 */
void Server_Print( Server* srv);

/** @brief Returns the Client ID (position) from a MAC
 * @param cli_array Client array
 * @param max_cli_id Maximum client ID
 * @param mac MAC adress
 * @return client ID or -1
 */
int Server_getClientfromMAC( Client* cli_array, int max_cli_id, char* mac );

/** @brief Strictifies a given Coord
 * @param coord Coord pointer
 * @return void
 */
void Server_StrictCoord( Coord* coord );

/** @brief Inits a Client array
 * @param cli_array Client array pointer
 * @param max_client_id Maximum Client id
 */
void Server_InitClientArray( Client* cli_array, int max_client_id );

/** @brief Updates a Client using a status packet, creates a new client entry if required
 * @param cli_array Client array
 * @param max_cli_id Maximum Client ID
 * @param stat Status packet pointer
 * @return -1 on error, 1 if the Client was late, or 0
 */
int Server_UpdateClient( Client* cli_array, int max_cli_id, StatusPacket* stat );

/** @brief Tries to fill a Status Packet using a buffer (from either UDP Client
 * or TCP Neighbour source)
 * @param buff Buffer
 * @param buff_sz Buffer size
 * @param stat Status Packet pointer
 * @param ip IP address pointer
 * @return -1 on error
 */
int Server_FillStatusPacket( char* buff, int buff_sz, StatusPacket* stat, char* ip );

/** @brief Fills a buffer from a file descriptor
 * @param fd File descriptor
 * @param buffer Buffer pointer
 * @param buff_sz Buffer size
 * @param socka Optionnal sockaddr struture pointer (used in UDP mode)
 * @return -1 on error
 */
int Server_FillBufferfromNetw( int fd, char* buffer, int buff_sz, struct sockaddr* socka );

/** @brief Send a buffer to a file descriptor
 * @param fd File descriptor
 * @param buffer Buffer pointer
 * @param buff_sz Size to send, from the beginning of the buffer
 * @param socka Optionnal sockaddr struture pointer (used in UDP mode)
 * @return -1 on error
 */
int Server_SendtoNetwfromBuffer( int fd, char* buffer, int sz_tosend, struct sockaddr* socka );

/** @brief Sends (forwards) a Status Packet to a Server
 * @param srv Target Server
 * @param stat Packet to send
 * @return -1 on error
 */
int Server_ForwardPackettoServer( Server* srv, StatusPacket* stat );

/** @brief Sends info about a Client to a Visu client
 * @param fd Visu client socket (file descriptor)
 * @param cli_array Client array
 * @param cli_id ID of Client to send
 * @return -1 on error
 */
int Server_UpdateVisufromClient( int fd, Client* cli_array, int cli_id );

/** @brief Sends the 'Authority' packet to a Visu client
 * @param fd Visu client socket (file descriptor)
 * @param srv Provides the responsability (authority) zone to send
 * @return -1 on error
 */
int Server_SendAuthoritytoVisu( int fd, Server* srv );

/** @brief Sends a reply to a Client (Client* version)
 * @param cli Client pointer
 * @param base Basic reply to send
 * @param ext Optionnal extension to base 
 * @return -1 on error
 */
int Server_ReplytoClientC( Client* cli, const char* base, const void* ext );

/** @brief Sends a reply to a Client (StatusPacket* version)
 * @param stat A Status Packet from a Client
 * @param base Basic reply to send
 * @param ext Optionnal extension to base
 * @return -1 on error
 *
 */
int Server_ReplytoClientSP( StatusPacket* stat, const char* base, const void* ext );

/** @brief Disambiguation : see Server_ReplytoClientC and Server_ReplytoClientSP
 */
int _Server_ReplytoClient( char* ip, unsigned int port, const char* base, const void* ext );

//@}

#endif
